package rgbos.forum.serverClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.Vector;
import rgbos.forum.domain.Auditor;
import rgbos.forum.domain.ControlFacade;
import rgbos.forum.domain.Post;
import rgbos.forum.domain.Topic;
import rgbos.forum.domain.User;
import rgbos.forum.exceptions.AdminException;
import rgbos.forum.exceptions.UserExeception;
import rgbos.forum.search.PostRate;


public class ClientHandler extends Thread{
	private ControlFacade contFacade;
	private User user_;
//	private ForumCtrlUser fcUser_;
//	private ForumCtrlPost fcPost_;
//	private ForumCtrlTopic fcTopic_;
	private  Socket clientSocket_;
	/** input from the client */
	private BufferedReader in_;
	/** output to the client */
	private PrintWriter out_;

//ui
	public ClientHandler( Socket socket,ControlFacade facade){
		this.clientSocket_ = socket;
//		this.fcUser_ = fcu;
//		this.fcPost_ = fcp;
//		this.fcTopic_ = fct;
		this.user_ = new User("guest","pass","aa",-1);
		this.contFacade = facade;
	}

	public void run() {
		try {
			initSocketIO();
		} catch (IOException e) {
			Auditor .Log("failed to initialize IO ", this.getClass());

			e.printStackTrace();
		}
		try {
			process();
		} catch (Exception e) {
			Auditor.Log("failure in exchanging massages", this.getClass());
			//e.printStackTrace();

		}
		close();


	}

	private void initSocketIO() throws IOException {
		this.in_ = new BufferedReader(new InputStreamReader(this.clientSocket_.getInputStream()));
		this.out_ = new PrintWriter(this.clientSocket_.getOutputStream(), true);
	}

	//
	private void process() throws IOException{
		String msg;
		String response =null;
		while ((msg = this.in_.readLine()) != null){

			Auditor.Log("Received \"" + msg + "\" from client", this.getClass());
			if( msg.equals("benny gay")){    //end massage
				break;
			}
			response =processMessage(msg);
			//response.replaceAll("<br>", "\newline");
			System.out.println(response);
			this.out_.println(response);
			//System.out.println("after sending"+ response);

		}
	//	this.fcUser_.logOut

		Auditor.Log("end of client connection :"+this.clientSocket_.getInetAddress(), this.getClass());

	}


//################################## Process message######################################
	private String processMessage(String msg) {
		String [] requestDetails  = msg.split(" ");
		if(requestDetails[0].equals("LOGIN")){
			return processLogIn(requestDetails);
		}
		if(requestDetails[0].equals("LOGOUT")){
			return processLogOut();
		}
		if(requestDetails[0].equals("REGISTER")){
			return processRegister(requestDetails);
		}
		if(requestDetails[0].equals("ENTER")){
			return processEnter(requestDetails[1]);
		}
		if(requestDetails[0].equals("PREM")){
			return processPrem(requestDetails[1]);
		}
		if(requestDetails[0].equals("ADD")){
			StringTokenizer strToken = new StringTokenizer(msg,"|||");
			strToken.nextToken();
			if(requestDetails[1].equals("TOPIC")){
				String str = strToken.nextToken();
				return processAddTopic(str);
			}
			if(requestDetails[1].equals("POST")){
				return processAddPost(requestDetails[2],strToken.nextToken(),strToken.nextToken());
			}
		}
		if(requestDetails[0].equals("SET")){
			if(requestDetails[1].equals("USER")){
				return processgetMail();
			}
			if(requestDetails[1].equals("MODERATOR")){ 
				return processSetModerator(requestDetails[2]);	
			}
		}
		if(requestDetails[0].equals("REMOVE")){
			if(requestDetails[1].equals("USER")){
				return processRemoveUser(requestDetails[2]);
			}
			if(requestDetails[1].equals("MODERATOR")){ 
				return processRemoveModerator(requestDetails[2]);	
			}
		}
		
		if(requestDetails[0].equals("SEARCH")){
			StringTokenizer strToken = new StringTokenizer(msg,"|||");
			strToken.nextToken();
			if(requestDetails[1].equals("CONTENT")){
				return processSearchContent(strToken.nextToken());
			}
			if(requestDetails[1].equals("DATE")){
				return processSearchDate(strToken.nextToken());
			}
			if(requestDetails[1].equals("DATES")){
				return processSearchDates(strToken.nextToken(),strToken.nextToken());
			}
			if(requestDetails[1].equals("AUTHOR")){
				return processSearchAuthor(strToken.nextToken());
			}
		}
		if(requestDetails[0].equals("GET")){
			if(requestDetails[1].equals("TOPIC")){
				return processGetTopic(requestDetails[2]);
			}
			if(requestDetails[1].equals("TOPICS")){
				if (requestDetails[2].equals("CURRENT")){
					return processTopicsGetCurrent();
				}
			}
			if(requestDetails[1].equals("MAIL")){
				return processgetMail();
			}
			if(requestDetails[1].equals("USERS")){
				return processGetAllUsers();
			}
			if(requestDetails[1].equals("ONLINE")){
				return processOnlineUsers();
			}
		}
		if(requestDetails[0].equals("EDIT")){
			System.out.println(msg);
			if(requestDetails[1].equals("MAIL")){
				
				return processEditUser(user_.getUsername(),user_.getPassword(),requestDetails[2]);
			}
			if(requestDetails[1].equals("USER")){
				return processEditUser(requestDetails[2],requestDetails[3],requestDetails[4]);
			}
			String [] editDetail  = msg.split("~~~");

			try {
				this.contFacade.editPost(Integer.parseInt(editDetail[1]),editDetail[2],editDetail[3]);
			} catch (NumberFormatException e) {
				Auditor.Log("fail to parse topic number", this.getClass());
				return "FAIL EDIT";
			} catch (UserExeception e) {
				return "FAIL UNAUTHORIZED";

			}
			//System.out.println("got to end of edit");
			return "SUCC";
		}
		if(requestDetails[0].equals("DELETE")){
			if(requestDetails[1].equals("TOPIC")){
				try{
					int topicId =Integer.parseInt(requestDetails[2]);
					return processDeleteTopic(topicId);
				}
			 catch (NumberFormatException e) {
				Auditor.Log("fail to parse topic number", this.getClass());
				return "FAIL DELETE PARSE";
			 }
			}else{
				try {
					this.contFacade.deletePost(Integer.parseInt(requestDetails[2]));
				} catch (NumberFormatException e) {
					Auditor.Log("fail to parse topic number", this.getClass());
					return "FAIL DELETE PARSE";
				}
				return "SUCC DELETE";
			}
		}
		return "";

	}







//##################################End of process message################################################

	private String processGetTopic(String postId) {
		int postIndex = Integer.parseInt(postId);
		Topic top = contFacade.getTopicByPostId(postIndex);
		return processEnter(String.valueOf(top.getId_()));
		
	}
	private String processRemoveModerator(String name) {
		User user = contFacade.getUserByName(name);
		user.setPermmisionGroup(0);
		if(contFacade.modifyUser(user)){
			return "SUCC REM MODERATOR "+name;
		}
		return "FAIL REM MODERATOR "+name;
	}

	private String processRemoveUser(String name) {
		if(this.contFacade.removeUser(name)){
			return "SUCC REM USER "+name;
		}
		return "FAIL REM USER "+name;
	}

	private String processPrem(String userName) {
		int prem = this.contFacade.checkPrems(userName);
		return "SUCC "+prem;
	}

	private String processSetModerator(String userName) {
		try {
			if(this.contFacade.makeModerator(userName)){
				return "SUCC";
			}
		} catch (AdminException e) {
			return "FAIL";
		}
		return "FAIL";
	}

	

	private String processDeleteTopic(int topicId) {
		Topic top  = this.contFacade.getTopicById(topicId);
		for(int posIndex: top.getPostVector()){
			if(!this.contFacade.deletePost(posIndex)){
				return "FAIL DELETE TOPIC POST'S";
			}
		}
		if(this.contFacade.deleteTopic(topicId)){
			return "SUCC DELETE TOPIC";
		}else{
			return "FAIL DELETE TOPIC";
		}
		
	}

	private String processSearchAuthor(String authorName) {
		Vector<PostRate> searchResult = this.contFacade.getSearchEngine().searchByAuthor(authorName);
		if(searchResult.size()==0){
			return "FAIL";
		}
		return postRateToString(searchResult);
	}

	private String processSearchDates(String fromDate, String toDate) {
		Vector<PostRate> searchResult = this.contFacade.getSearchEngine().searchByDate(fromDate, toDate);
		if(searchResult.size()==0){
			return "FAIL";
		}
		return postRateToString(searchResult);
	}
	private String processSearchDate(String date) {
		Vector<PostRate> searchResult = this.contFacade.getSearchEngine().searchByDate(date);
		if(searchResult.size()==0){
			return "FAIL";
		}
		return postRateToString(searchResult);
	}
	private String processSearchContent(String content) {
		Vector<PostRate> searchResult = this.contFacade.getSearchEngine().searchByContent(content);
		if(searchResult.size()==0){
			return "FAIL";
		}
		return postRateToString(searchResult);
	}
	private String processGetAllUsers() {
		Vector<User> allUsers = this.contFacade.getAllUsers();
		return userToString(allUsers);
	}
	private String processOnlineUsers() {
		Vector<User> allUsers = this.contFacade.getOnlineUsers();
		return userToString(allUsers);
	}

	

	private String processLogOut() {
		this.contFacade.logOut(this.user_.getUsername());
		return "SUCC";
	}
	private String processEditUser(String userName, String pass, String mail) {
		User user= new User(userName,pass,mail,this.user_.getPermmisionGroup());
		if( this.contFacade.modifyUser(user)){
			this.user_ = user;
			return "SUCC";
		}
		return "FAIL";
	}
	private String processgetMail() {
		if(this.user_.getPermmisionGroup()==-1){
			return "FAIL";
		}
		return this.user_.getEmail();
	}

	private String processAddPost(String topicId,String title, String body) {
		System.out.println(" in add post");
		Post post = new Post(0,title,this.user_.getUsername(),new Date(System.currentTimeMillis()),body);
		try {
			if(this.contFacade.addNewPost(post, Integer.parseInt(topicId))!= -1){
				return "SUCC";
			}
		} catch (NumberFormatException e) {
			return "FAIL";
		}
		return "FAIL";

	}

	private String processAddTopic(String title) {
		Topic top = new Topic(0,title,this.user_.getUsername(),new Date(System.currentTimeMillis()),
				new Vector<Integer>(), 0, 0);
		System.out.println("the new topic num is: "+this.contFacade.addNewTopic(top));
		return "SUCC ADD TOPIC";
	}

	////////////////////////Topic/////////////////////
	private String processTopicsGetCurrent() {
		Vector<Topic> allTopics =  this.contFacade.showNewTopics();
		Collections.sort(allTopics);
		return topicsToString(allTopics);

	}

////////////////////////////Post/////////////////////////////////

	private String processEnter(String topicId) {
		
		Vector<Post> posts = this.contFacade.enterTopic(Integer.parseInt(topicId));
		if(posts == null){ System.out.println("posts came back null");}
		return  postsToString(posts);
	}

////////////////////////////// end Post////////////////

	private String processRegister(String[] requestDetails) {
		if(this.contFacade.checkUserExist(requestDetails[1]))
			return "FAIL REGISTER EXIST";
		this.user_ = new User(requestDetails[1],requestDetails[2],requestDetails[3],0);
		try {
			boolean logged = this.contFacade.registerNewUser(this.user_);
			if(logged){

				Auditor.Log("user "+requestDetails[1]+"has registered", this.getClass());
				return "SUCC REGISTER";
			}else { return "FAIL REGISTER " +requestDetails[1];}
		} catch (Exception e) {
			Auditor.Log("exception in log in", this.getClass());
			return "FAIL REGISTER "+requestDetails[1];

		}
	}

	private String processLogIn(String[] requestDetails){
		try {
			boolean logged = this.contFacade.logIn(requestDetails[1], requestDetails[2]);
			if(logged){
				this.user_ = new User(requestDetails[1],requestDetails[2], this.contFacade.getMailByUserName(requestDetails[1]),
					this.contFacade.checkPrems(requestDetails[1]));
				if(this.user_.getPermmisionGroup()==0){
					Auditor.Log("User "+requestDetails[1]+"has logged in", this.getClass());
				}else{
					if(this.user_.getPermmisionGroup()==1){
						Auditor.Log("Moderator "+requestDetails[1]+"has logged in", this.getClass());
					}else{
						Auditor.Log("Admin "+requestDetails[1]+"has logged in", this.getClass());
					}
				}
				return "SUCC LOGIN " +requestDetails[1]+" "+this.contFacade.checkPrems(requestDetails[1]);
			}else { return "FAIL LOGIN PASS "+requestDetails[1];}
		} catch (Exception e) {
			Auditor.Log("exception in log in", this.getClass());
			return "FAIL LOGIN USER "+ requestDetails[1];

		}
	}


	private void close(){
		try {
			if (this.in_ != null)
			{
				this.in_.close();
			}
			if (this.out_ != null)
			{
			this.out_.close();
			}

			this.clientSocket_.close();
		}
		catch (Exception e)
		{
			Auditor.Log("Exception in closing I/O",this.getClass());
		}
	}
	private String topicsToString(Vector<Topic> topics) {
		String response = "";
		for(Topic top: topics){
			response += top.getTopicAsString();
			response += Forum.DELIMITER;
		}
		return response;
	}
	
	private String postRateToString(Vector<PostRate> posts){
		String response = "";
		for(PostRate pos: posts){
			response += pos.getPost().getElementAsString();
			response += "~~~"+String.valueOf(pos.getScore());
			response += Forum.DELIMITER;
		}
		return response.replaceAll("\n","<br>").replaceAll("\r", "");
	}
	private String postsToString(Vector<Post> posts){
		String response = "";
		for(Post pos: posts){
			response += pos.getElementAsString();
			response += Forum.DELIMITER;
		}
		return response.replaceAll("\n","<br>").replaceAll("\r", "");
	}
	private String userToString(Vector<User> allUsers) {
		String answer = "";
		for (User tmpUser : allUsers) {
			answer+=(tmpUser.getUsername());
			answer+= "~~~"+tmpUser.getPermmisionGroup();
			answer+="|||";
		}
		return answer;
	}
}



